﻿using System;
using System.Collections.Generic;
using System.Data;
using EventHandler.Libraries.Business.Security;
using NUnit.Framework;
using Rhino.Mocks;
using EventHandler.Libraries.Business.Database;

namespace EventHandler.Libraries.Business.Tests.Security.AccountTests
{
    [TestFixture]
    public class CreateAccountTests
    {

		private void CreateAccount(IAccount testAccount)
		{
			CreateAccount(testAccount, 9999);
		}

		private void CreateAccount(IAccount testAccount, int testAccountID)
		{
			IDatabaseHelper dbHelper = AccountTestUtilities.CreateDatabaseHelper();
			var helper = new AccountHelper(dbHelper);

			var cnnUserExists = MockRepository.GenerateMock<IDbConnection>();
			dbHelper.Expect(x => x.GetConnection()).Return(cnnUserExists);

			var cmdUserExists = MockUtilities.CreateMockCommand(cnnUserExists);
			var paramUsername = MockUtilities.CreateMockParameter(cmdUserExists);
			
			var cnnCreateAccount = MockRepository.GenerateMock<IDbConnection>();
			dbHelper.Expect(x => x.GetConnection()).Return(cnnCreateAccount);

			var cmdCreateAccount = MockUtilities.CreateMockCommand(cnnCreateAccount);
			var paramNewFirstname = MockUtilities.CreateMockParameter(cmdCreateAccount);
			var paramNewLastname = MockUtilities.CreateMockParameter(cmdCreateAccount);
			var paramNewEmail = MockUtilities.CreateMockParameter(cmdCreateAccount);
			var paramNewPassword = MockUtilities.CreateMockParameter(cmdCreateAccount);
			var paramNewUsername = MockUtilities.CreateMockParameter(cmdCreateAccount);
			var paramNewAuthenticationTypeID = MockUtilities.CreateMockParameter(cmdCreateAccount);
			var paramNewAccountId = MockUtilities.CreateMockParameter(cmdCreateAccount);
			paramNewAccountId.Value = testAccountID;

			var drUserExists = MockRepository.GenerateMock<IDataReader>();
			cmdUserExists.Expect(x => x.ExecuteReader()).Return(drUserExists);
			drUserExists.Expect(x => x.Read()).Return(false);

			IAccount createdAccount = helper.CreateAccount(testAccount);
			
			cnnUserExists.AssertWasCalled(x => x.Open());
			cnnUserExists.AssertWasCalled(x => x.Close());

			cnnCreateAccount.AssertWasCalled(x => x.Open());
			cnnCreateAccount.AssertWasCalled(x => x.Close());
			cmdCreateAccount.AssertWasCalled(x => x.ExecuteNonQuery());

			Assert.AreEqual(testAccount.FirstName, createdAccount.FirstName);
			Assert.AreEqual(testAccount.LastName, createdAccount.LastName);
			Assert.AreEqual(testAccount.Email, createdAccount.Email);
			Assert.AreEqual(testAccount.Password, createdAccount.Password);
			Assert.AreEqual(testAccount.Username, createdAccount.Username);
			Assert.AreEqual(testAccount.Properties, createdAccount.Properties);
			Assert.AreEqual(testAccountID, createdAccount.AccountId);

			Assert.AreEqual("Auth.GetUserByUserName", cmdUserExists.CommandText);
			Assert.AreEqual(CommandType.StoredProcedure, cmdUserExists.CommandType);
			
			Assert.AreEqual("@UserName", paramUsername.ParameterName);
			Assert.AreEqual(testAccount.Username, paramUsername.Value);
			Assert.AreEqual(255, paramUsername.Size);
			Assert.AreEqual(DbType.String, paramUsername.DbType);

			Assert.AreEqual("Auth.CreateNewAccount", cmdCreateAccount.CommandText);
			Assert.AreEqual(CommandType.StoredProcedure, cmdCreateAccount.CommandType);

			Assert.AreEqual("@AuthenticationSignInName", paramNewUsername.ParameterName);
			Assert.AreEqual(testAccount.Username, paramNewUsername.Value);
			Assert.AreEqual(255, paramNewUsername.Size);
			Assert.AreEqual(DbType.String, paramNewUsername.DbType);

			Assert.AreEqual("@FirstName", paramNewFirstname.ParameterName);
			Assert.AreEqual(testAccount.FirstName, paramNewFirstname.Value);
			Assert.AreEqual(30, paramNewFirstname.Size);
			Assert.AreEqual(DbType.String, paramNewFirstname.DbType);

			Assert.AreEqual("@LastName", paramNewLastname.ParameterName);
			Assert.AreEqual(testAccount.LastName, paramNewLastname.Value);
			Assert.AreEqual(30, paramNewLastname.Size);
			Assert.AreEqual(DbType.String, paramNewLastname.DbType);

			Assert.AreEqual("@EmailAddress", paramNewEmail.ParameterName);
			Assert.AreEqual(testAccount.Email, paramNewEmail.Value);
			Assert.AreEqual(255, paramNewEmail.Size);
			Assert.AreEqual(DbType.String, paramNewEmail.DbType);

			Assert.AreEqual("@AuthenticationTypeID", paramNewAuthenticationTypeID.ParameterName);
			Assert.AreEqual(testAccount.AuthenticationTypeId, paramNewAuthenticationTypeID.Value);
			Assert.AreEqual(DbType.Int16, paramNewAuthenticationTypeID.DbType);

			Assert.AreEqual("@AccountID", paramNewAccountId.ParameterName);
			Assert.AreEqual(testAccountID, paramNewAccountId.Value);
			Assert.AreEqual(DbType.Int32, paramNewAccountId.DbType);
		}
		
		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CreateAccount_Should_Fail_If_Account_Null()
		{
			CreateAccount(null);
		}


        [Test]
        public void CreateAccount_Should_Succeed_If_Account_Valid()
        {
			CreateAccount(AccountTestUtilities.GetValidTestAccount());
        }

		[Test]
		[ExpectedException(typeof(ApplicationException))]
		public void CreateAccount_Should_Faild_If_CreateAccount_Returns_Negative_1()
		{
			CreateAccount(AccountTestUtilities.GetValidTestAccount(), -1);
		}

        [Test]
        [ExpectedException(typeof(ArgumentException))]
        public void CreateAccount_Should_Fail_If_Account_Data_Empty()
        {
            IAccount account = new Account
            {
                FirstName = "",
                LastName = "",
                Email = "",
                Username = "",
                Password = "",
				AuthenticationTypeId = 0,
                Properties = new Dictionary<string, string>()
            };
			CreateAccount(account);
        }

		[Test]
		[ExpectedException(typeof(ArgumentException))]
		public void CreateAccount_Should_Fail_If_LastName_Null()
		{
            IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.LastName = null;
			CreateAccount(testAccount);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException))]
		public void CreateAccount_Should_Fail_If_FirstName_Null()
        {
            IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.FirstName = null;
			CreateAccount(testAccount);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException))]
		public void CreateAccount_Should_Fail_If_Email_Null()
        {
            IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.Email = null;
			CreateAccount(testAccount);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException))]
		public void CreateAccount_Should_Fail_If_Username_Null()
        {
           	IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.Username = null;
			CreateAccount(testAccount);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException))]
		public void CreateAccount_Should_Fail_If_Password_Null()
        {
           	IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.Password = null;
			CreateAccount(testAccount);
		}

		[Test]
		public void CreateAccount_Should_Succeed_If_Properties_Null()
        {
			IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.Properties = null;
			CreateAccount(testAccount);
		}

        [Test]
		[ExpectedException(typeof(ArgumentException))]
        public void TestPasswordStrength_On_abcde()
        {
			IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.Password = "abcde";
			CreateAccount(testAccount);
        }

		[Test]
        public void TestPasswordStrength_On_123abc()
        {
			IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.Password = "123abc";
			CreateAccount(testAccount);
        }

		[Test]
		[ExpectedException(typeof(ArgumentException))]
        public void TestPasswordStrength_On_abcdef()
        {
            var helper = new AccountHelper(AccountTestUtilities.CreateDatabaseHelper());
			IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.Password = "abcdef";
			CreateAccount(testAccount);
        }
		
		[Test]
        public void TestPasswordStrength_On_abcde1()
        {
			IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.Password = "abcde1";
			CreateAccount(testAccount);
        }
		
		[Test]
		[ExpectedException(typeof(ArgumentException))]
        public void TestPasswordStrength_On_12345()
        {
			IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.Password = "12345";
			CreateAccount(testAccount);
        }

		[Test]
		[ExpectedException(typeof(ArgumentException))]
        public void TestPasswordStrength_On_123456()
        {
			IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.Password = "123456";
			CreateAccount(testAccount);
        }

		[Test]
		[ExpectedException(typeof(ArgumentException))]
        public void TestPasswordStrength_On_Empty_String()
        {
			IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.Password = "";
			CreateAccount(testAccount);
        }

        [Test]
        public void CreateAccount_Should_Fail_If_Username_Already_Exists()
        {
			IDatabaseHelper dbHelper = AccountTestUtilities.CreateDatabaseHelper();
			var helper = new AccountHelper(dbHelper);
			
			var cn = MockRepository.GenerateMock<IDbConnection>();
			dbHelper.Expect(x => x.GetConnection()).Return(cn);
			
			var cmd = MockRepository.GenerateMock<IDbCommand>();
			cn.Expect(x => x.CreateCommand()).Return(cmd);
			cmd.Expect(x => x.CommandText).PropertyBehavior();
			cmd.Expect(x => x.CommandType).PropertyBehavior();
			
			var username = MockRepository.GenerateMock<IDbDataParameter>();
			cmd.Expect(x => x.CreateParameter()).Return(username);
			username.Expect(x => x.Size).PropertyBehavior();
			username.Expect(x => x.ParameterName).PropertyBehavior();
			username.Expect(x => x.Value).PropertyBehavior();
			username.Expect(x => x.DbType).PropertyBehavior();

			var dr = MockRepository.GenerateMock<IDataReader>();
			cmd.Expect(x => x.ExecuteReader()).Return(dr);
			dr.Expect(x => x.Read()).Return(true);
			
			var parameters = MockRepository.GenerateMock<IDataParameterCollection>();
			cmd.Expect(x => x.Parameters).Return(parameters);

			IAccount testAccount = AccountTestUtilities.GetValidTestAccount();
			testAccount.Username = "IExist";
			
			try
			{
				helper.CreateAccount(testAccount);
				Assert.Fail("Expected an Argument Exception since the userName should exist already");
			}
			catch (ArgumentException)
			{ 
				//expected exception since we're forcing a failure
			}
			
			cn.AssertWasCalled(x => x.Open());
			cn.AssertWasCalled(x => x.Close());
			parameters.AssertWasCalled(x => x.Add(username));

			Assert.AreEqual("Auth.GetUserByUserName", cmd.CommandText);
			Assert.AreEqual(CommandType.StoredProcedure, cmd.CommandType);
			Assert.AreEqual("@UserName", username.ParameterName);
			Assert.AreEqual(testAccount.Username, username.Value);
			Assert.AreEqual(255, username.Size);
			Assert.AreEqual(DbType.String, username.DbType);
			Assert.IsFalse(dr.Read(), "Expected the DataReader to return a value");
				
        }
    }

}
